/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.scribeplugin;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.LayoutStyle;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.pearreview.ui.*;
import com.pearreview.ui.clock.ScribeClock;
import com.pearreview.application.Messages;
import com.pearreview.application.SpringGlassPane;
import com.pearreview.util.PDFFileFilter;
import com.pearreview.util.review.Meeting;
import com.pearreview.util.review.Review;

public class ExportPanel extends PearPanel implements ScribePanel {
	
	private static final long serialVersionUID = -7513244748922392332L;
	private FormLabel commentsLabel;
	private JScrollPane scrollPane1;
	private FadingTextArea comments;
	private JScrollPane scrollPane2;
	private FadingTextArea recommendation;
	private FormLabel recommendationLabel;
	private JScrollPane scrollPane3;
	private FadingTextArea canceled;
	private FormLabel canceledLabel;
	private JButton exportProtocolBtn;
	private JButton exportTracBtn;
	private JPanel container;
	private HeadingLabel heading;
	private JCheckBox abortCheck;
	private JCheckBox signatureCheck;
	private JCheckBox allMeetingsCheck;
	private JCheckBox printImagesCheck;
	
	private File lastDir;
	
	private Review review;
	private Meeting meeting;
	
	private SpringGlassPane glassPane;
	private BottomButtonPanel bottom;
	
	private ScribeReviewFormValidator validator;
	
	public ExportPanel(SpringGlassPane glassPane) {
		this.glassPane = glassPane;
		initComponents();
	}
	
	public void setReview(Review review) {
		this.review = review;
	}
	
	public void setMeeting(Meeting meeting) {
		this.meeting = meeting;
	}
	
	public void setLastExportFile(File lastDir) {
		this.lastDir = lastDir;
	}
	
	public File getLastExportFile() {
		return lastDir;
	}
	
	public boolean isCreateSignatureFieldsSelected() {
		return signatureCheck.isSelected();
	}
	
	public void setCreateSignatureFieldsSelected(boolean b) {
		signatureCheck.setSelected(b);
	}
	
	public void setExportOfAllMeetingsSelected(boolean b) {
		allMeetingsCheck.setSelected(b);
	}
	
	public boolean isExportOfAllMeetingsSelected() {
		return allMeetingsCheck.isSelected();
	}
	
	public boolean isPrintImagesSelected() {
		return printImagesCheck.isSelected();
	}
	
	public void setPrintImagesSelected(boolean b) {
		printImagesCheck.setSelected(b);
	}
	
	public static File getDefaultExportFile() {
		return new File("protocol_" + 
				new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
	}
	
	private void initComponents() {
		setFont(new Font("Tahoma", Font.PLAIN, 12));
		commentsLabel = new FormLabel();
		scrollPane1 = new JScrollPane();
		comments = new FadingTextArea();
		scrollPane2 = new JScrollPane();
		recommendation = new FadingTextArea();
		recommendationLabel = new FormLabel();
		scrollPane3 = new JScrollPane();
		canceled = new FadingTextArea();
		canceledLabel = new FormLabel();
		exportProtocolBtn = new JButton();
		exportTracBtn = new JButton();
		container = new JPanel();
		
		abortCheck = new JCheckBox();
		allMeetingsCheck = new JCheckBox();
		signatureCheck = new JCheckBox();
		printImagesCheck = new JCheckBox();
		
		ArrayList<JComponent> sameSizeBtns = new ArrayList<JComponent>();
		sameSizeBtns.add(exportTracBtn);
		sameSizeBtns.add(exportProtocolBtn);

		//======== this ========

		//---- commentsLabel ----
		commentsLabel.setText(getLocalizedString("commentsLabel.text"));
		
		comments.setFont(getFont());
		//======== scrollPane1 ========
		{
			scrollPane1.setViewportView(comments);
		}

		//======== scrollPane2 ========
		{
			scrollPane2.setViewportView(recommendation);
		}

		//---- recommendationLabel ----
		recommendationLabel.setText(
				getLocalizedString("recommendationLabel.text"));
		recommendation.setFont(getFont());

		//======== scrollPane3 ========
		{

			scrollPane3.setViewportView(canceled);
			canceled.setFont(getFont());
			canceled.setEnabled(false);
			canceled.addMouseListener(new MouseListener() {
				@Override
				public void mouseClicked(MouseEvent arg0) {
					if(!abortCheck.isSelected()) {
						canceled.requestFocus();
						abortCheck.doClick();
					}
				}
				@Override
				public void mouseEntered(MouseEvent arg0) { }
				@Override
				public void mouseExited(MouseEvent arg0) { }
				@Override
				public void mousePressed(MouseEvent arg0) { }
				@Override
				public void mouseReleased(MouseEvent arg0) { }
			});
		}

		//---- canceledLabel ----
		canceledLabel.setText(getLocalizedString("canceledLabel.text"));

		//---- exportProtocolBtn ----
		exportProtocolBtn.setAction(new CreateProtocolAction());
		exportProtocolBtn.setText(getLocalizedString("exportProtocolBtn.text"));
		exportProtocolBtn.setFont(getFont());
		exportProtocolBtn.setIcon(
				new ImageIcon(getClass().getResource("images/pdf.png")));

		//---- exportTracBtn ----
		exportTracBtn.setAction(new CreateTracProtocolAction());
		exportTracBtn.setText(getLocalizedString("exportTracBtn.text"));
		exportTracBtn.setFont(getFont());
		exportTracBtn.setIcon(
				new ImageIcon(getClass().getResource("images/trac.png")));
		
		//---- abortCheck ----
		abortCheck.setOpaque(false);
		abortCheck.setSelected(false);
		abortCheck.addActionListener(new ActionListener() {
			private boolean showed = false;
			@Override
			public void actionPerformed(ActionEvent e) {
				if(abortCheck.isSelected() && !showed)
					JOptionPane.showMessageDialog(ExportPanel.this, 
							getLocalizedString("information.cancel"), 
							getLocalizedString("information.cancel.title"), 
							JOptionPane.INFORMATION_MESSAGE);
				showed = true;
				canceled.setEnabled(abortCheck.isSelected());
			}
		});
		
		signatureCheck.setOpaque(false);
		signatureCheck.setText(getLocalizedString("signatureCheck.text"));
		signatureCheck.setFont(getFont());
		signatureCheck.setSelected(true);
		
		allMeetingsCheck.setOpaque(false);
		allMeetingsCheck.setText(getLocalizedString("allMeetingsCheck.text"));
		allMeetingsCheck.setFont(getFont());
		allMeetingsCheck.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				signatureCheck.setEnabled(!allMeetingsCheck.isSelected());
				
			}
			
		});
		
		printImagesCheck.setOpaque(false);
		printImagesCheck.setText(getLocalizedString("print_images"));
		printImagesCheck.setFont(getFont());
		printImagesCheck.setSelected(false);
		
		calcPreferredSize(sameSizeBtns);
		
		container.setOpaque(false);

		GroupLayout layout = new GroupLayout(container);

		container.setLayout(layout);
		layout.setHorizontalGroup(
			layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
					.addContainerGap()
					.addGroup(layout.createParallelGroup()
						.addComponent(commentsLabel, 
								GroupLayout.PREFERRED_SIZE, 
								137, 
								137)
						.addComponent(recommendationLabel, 
								GroupLayout.PREFERRED_SIZE, 
								137, 
								137)
						.addComponent(canceledLabel, 
								GroupLayout.PREFERRED_SIZE, 
								137, 
								137))
					.addGroup(layout.createParallelGroup()
						.addGroup(layout.createSequentialGroup()
							.addComponent(exportTracBtn, 
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE)
							.addGap(10)
							.addComponent(printImagesCheck)
							.addContainerGap())
						.addGroup(layout.createParallelGroup()
							.addGroup(layout.createSequentialGroup()
								.addComponent(exportProtocolBtn,
										GroupLayout.PREFERRED_SIZE,
										GroupLayout.PREFERRED_SIZE,
										GroupLayout.PREFERRED_SIZE)
								.addGap(10)
								.addComponent(signatureCheck)
								.addGap(10)
								.addComponent(allMeetingsCheck)
								.addContainerGap())
							.addGroup(layout.createSequentialGroup()
								.addGroup(layout.createParallelGroup(
										GroupLayout.Alignment.TRAILING)
										.addGroup(layout.createParallelGroup()
									.addComponent(scrollPane3,
											GroupLayout.DEFAULT_SIZE,
											300,
											Short.MAX_VALUE)
									.addComponent(abortCheck))
									.addComponent(scrollPane2, 
											GroupLayout.Alignment.LEADING, 
											GroupLayout.DEFAULT_SIZE, 
											300, 
											Short.MAX_VALUE)
									.addComponent(scrollPane1,
											GroupLayout.Alignment.LEADING, 
											GroupLayout.DEFAULT_SIZE, 
											300, 
											Short.MAX_VALUE))
								.addGap(10)))))
		);
		layout.setVerticalGroup(
			layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
					.addContainerGap()
					.addGroup(layout.createParallelGroup()
						.addComponent(scrollPane1, 
								GroupLayout.PREFERRED_SIZE, 
								100, 
								GroupLayout.PREFERRED_SIZE)
						.addComponent(commentsLabel, 
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
					.addGroup(layout.createParallelGroup()
						.addComponent(scrollPane2, 
								GroupLayout.PREFERRED_SIZE, 
								100, 
								GroupLayout.PREFERRED_SIZE)
						.addComponent(recommendationLabel,
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
					.addGroup(layout.createParallelGroup()
							.addGroup(layout.createSequentialGroup()
									.addComponent(abortCheck)
							.addComponent(scrollPane3, 
									GroupLayout.PREFERRED_SIZE, 
									100, 
									GroupLayout.PREFERRED_SIZE))
						.addComponent(canceledLabel, 
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								GroupLayout.PREFERRED_SIZE))
					.addGap(18, 18, 18)
					.addGroup(layout.createParallelGroup()
							.addComponent(exportProtocolBtn,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE)
							.addComponent(signatureCheck,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE)
							.addComponent(allMeetingsCheck,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE,
									GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
					.addGroup(layout.createParallelGroup()
							.addComponent(exportTracBtn)
							.addComponent(printImagesCheck))
					.addContainerGap(10, Short.MAX_VALUE))
		);
		
		setLayout(new BorderLayout());
		heading = new HeadingLabel(Messages.getString(
				"PearReview.Scribe.tabs.export"));
		add(heading, BorderLayout.NORTH);
		
		add(container, BorderLayout.CENTER);
		
		bottom = new BottomButtonPanel();
		bottom.setFontSliderEnabled(true);
		bottom.addFontSliderChangeListener(new FontChangeListener());
		add(bottom, BorderLayout.SOUTH);
	}
	
	public void applyChanges() {
		if(review != null) {
			review.setRecommendation(getRecommendation());
		}
		
		if(meeting != null)	{
			meeting.setCanceled(getCanceled());
			
			if(meeting.getProtocol() != null) {
				meeting.getProtocol().setComments(getComments());
			}
		}
	}
	
	@Override
	public void setClock(ScribeClock clock) {
	}
	
	public void setReviewValidator(ScribeReviewFormValidator validator) {
		this.validator = validator;
	}
	
	public String getComments() {
		return comments.getText();
	}
	public void setComments(String comments) {
		this.comments.setText(comments);
	}
	
	public String getRecommendation() {
		return recommendation.getText();
	}
	public void setRecommendation(String recommendation) {
		this.recommendation.setText(recommendation);
	}
	
	public String getCanceled() {
		return abortCheck.isSelected() ? canceled.getText() : null;
	}
	public void setCanceled(String canceled) {
		this.canceled.setText(canceled);
		abortCheck.setSelected(canceled != null);
	}
	
	private class CreateTracProtocolAction extends AbstractAction {
		private TracSettingsDialog csvDialog;
		private static final long serialVersionUID = -379956908059518404L;
		@Override
		public void actionPerformed(ActionEvent ae) {
			applyChanges();
			if(!validator.isValidReviewForm()) {
				validator.validateReviewForm();
				String message = 
						getLocalizedString("error.validation.trac");
				String title = 
						getLocalizedString("error.validation.trac.title");
				glassPane.setFaded(true);
				JOptionPane.showMessageDialog(ExportPanel.this, 
							message, 
							title, 
							JOptionPane.ERROR_MESSAGE);
				glassPane.setFaded(false);
				return;
			}
			
			if(csvDialog == null) {
				Component rootComp = SwingUtilities.getRoot(ExportPanel.this);
				if(rootComp instanceof Frame) {
					csvDialog = new TracSettingsDialog((Frame)rootComp);
				} else if (rootComp instanceof Dialog) {
					csvDialog = new TracSettingsDialog((Dialog)rootComp);
				} else
					csvDialog = new TracSettingsDialog((Frame)null);
				csvDialog.setModal(true);
			}
			
			csvDialog.setFindings(meeting.getProtocol().getFindings());
			csvDialog.setSeverities(review.getSeverities());
			
			glassPane.setFaded(true);
			csvDialog.setVisible(true);
			glassPane.setFaded(false);
		}
	}
	
	/**
	 * Returns localized string of this component
	 * @param key key of string to localize
	 * @return localized string of this component
	 */
	private static String getLocalizedString(String key) {
		return Messages.getString("PearReview.Scribe.export." + key);
	}
	
	/**
	 * If true, sets the loading panel with animation to the bottom of the 
	 * current panel and removes instead the bottom navigation buttons.
	 * 
	 * Otherwise removes the loading panel and puts the navigation buttons
	 * back.
	 * 
	 * @param loading
	 */
	private void setLoading(boolean loading) {
		if (loading) {
			glassPane.setLoadingMessage(getLocalizedString("exporting"));
			glassPane.showLoadingPanel(true);
		} else {
			glassPane.showLoadingPanel(false);
		}
	}
	
	private class CreateProtocolAction extends AbstractAction {
		private static final long serialVersionUID = -3709598163071113321L;
		@Override
		public void actionPerformed(ActionEvent e) {
			applyChanges();
			if(!validator.isValidReviewForm()) {
				validator.validateReviewForm();
				String message = 
						getLocalizedString("error.validation.protocol");
				String title = 
						getLocalizedString("error.validation.protocol.title");
				glassPane.setFaded(true);
				JOptionPane.showMessageDialog(ExportPanel.this, 
							message, 
							title, 
							JOptionPane.ERROR_MESSAGE);
				glassPane.setFaded(false);
				return;
			}
			
			final JFileChooser saveDialog = new JFileChooser();
			saveDialog.setMultiSelectionEnabled(false);
			saveDialog.setAcceptAllFileFilterUsed(false);
			
			PDFFileFilter pdfFilter = new PDFFileFilter();
			
			saveDialog.setFileFilter(pdfFilter);
			saveDialog.setCurrentDirectory(lastDir);
			
			saveDialog.setSelectedFile(
					lastDir == null ? getDefaultExportFile() : lastDir);
			
			glassPane.setFaded(true);
			int ret = saveDialog.showSaveDialog(ExportPanel.this);
			glassPane.setFaded(false);
			
			if (JFileChooser.APPROVE_OPTION != ret)
				return;
			
			lastDir = saveDialog.getSelectedFile();
			if(!pdfFilter.accept(lastDir)) {
				String fileName = lastDir.getName();
				fileName += PDFFileFilter.EXTENSION;
				lastDir = new File(lastDir.getParent(), fileName);
			}
			
			final ProtocolWriter docCreator;
			
			docCreator = new PDFProtocolWriter(true);
			
			SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
				@Override
				protected Void doInBackground() throws Exception {
					setLoading(true);
					
					FileOutputStream fos;
					BufferedOutputStream bos = null;
					
					docCreator.setCreateSignatureFields(
							signatureCheck.isSelected());
					docCreator.setReviewProtocol(
							allMeetingsCheck.isSelected());
					docCreator.setPrintImages(printImagesCheck.isSelected());
			
			
					try {
						fos = new FileOutputStream(lastDir);
						bos = new BufferedOutputStream(fos);
						docCreator.write(review, meeting, fos);
						
						bos.close();
						
						Desktop.getDesktop().browse(lastDir.toURI());
					} catch (FileNotFoundException e1) {
						JOptionPane.showMessageDialog(ExportPanel.this,
								//TODO localize etc.
								e1.getMessage(), 
								Messages.getString("PearReview.error"), 
								JOptionPane.ERROR_MESSAGE);
					} catch (Exception e1) {
						// TODO error message?!
						e1.printStackTrace();
						try {
							bos.close();
						} catch (IOException e2) {
							JOptionPane.showMessageDialog(ExportPanel.this,
									//TODO localize etc. 
									e2.getMessage(), 
									Messages.getString("PearReview.error"),
									JOptionPane.ERROR_MESSAGE);
						}
						JOptionPane.showMessageDialog(ExportPanel.this,
								//TODO localize etc. 
								e1.getMessage(),
								Messages.getString("PearReview.error"),
								JOptionPane.ERROR_MESSAGE);
					}
					
					setLoading(false);
					return null;
				}
			};
			
			worker.execute();
		}
	}

	@Override
	public String getHint() {
		return Messages.getString("PearReview.hint.scribe.export");
	}

	@Override
	public boolean isValidForm() {
		return true;
	}

	@Override
	public String getErrorMessage() {
		return null;
	}
	
	private Dimension calcPreferredSize(ArrayList<JComponent> list) {
		Dimension biggestDim = new Dimension(1,1);
		for (JComponent comp: list) {
			Dimension currentDim = comp.getPreferredSize();
			if (currentDim.height > biggestDim.height)
				biggestDim.height = currentDim.height;
			if (currentDim.width > biggestDim.width)
				biggestDim.width = currentDim.width;
		}
		
		for (JComponent comp: list) {
			comp.setPreferredSize(biggestDim);
		}
		
		return biggestDim;
	}
	
	private class FontChangeListener implements ChangeListener, Serializable {
		private static final long serialVersionUID = -1971767478503395523L;
		@Override
		public void stateChanged(ChangeEvent e) {
			updateFontSize();
		}
	}
	
	private void updateFontSize() {
		if(bottom != null) {
			int size = bottom.getFontSize();
			setFontToComponents(this, size);
		}
	}
	
	private void setFontToComponents(Container comp, int fontSize) {
		//comp.setFont(new Font("Tahoma", Font.PLAIN, fontSize));
			
		Font font = new Font(comp.getFont().getFontName(), 
				comp.getFont().getStyle(),
				fontSize );
		comp.setFont(font);
		for(int i = 0 ; i < comp.getComponentCount(); i++) {
			Component c = comp.getComponent(i);
			if(c == bottom 
					|| c == heading
					|| c instanceof JLabel
					|| c instanceof JButton
					|| c instanceof JCheckBox)
				continue;
			
			if(c instanceof JTable) {
				JTable table = (JTable) c;
				table.setRowHeight(table.getRowMargin() + 
						table.getFontMetrics(table.getFont()).getHeight());
			}
			
			if(c instanceof Container) {
				setFontToComponents((Container)c, fontSize);
			}
		}
	}

	@Override
	public void setNextViewAction(Action action) {
		bottom.setNextViewAction(action);
	}

	@Override
	public void setPreviousViewAction(Action action) {
		bottom.setPreviousViewAction(action);
	}

	@Override
	public int getFontSize() {
		return bottom.getFontSize();
	}

	@Override
	public void setFontSize(int fontSize) {
		if(fontSize >= 0)
			bottom.setFontSize(fontSize);
	}
}
